package com.fourforfo.fourmall.product.web;

import com.fourforfo.fourmall.product.service.CategoryService;
import org.apache.ibatis.annotations.Param;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.UUID;
import java.util.concurrent.TimeUnit;



/**
 * 测试Redisson的重入锁，读写锁，信号量等
 * 注意Redisson的所有锁都是分布式锁，基于Redis实现
 */
@Controller
public class TestLockController {

    @Autowired
    RedissonClient redissonClient;

    @Autowired
    CategoryService categoryService;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    TestCache testCache;

    public class OtherCache {
        public void testInvokeOtherMethod(){
            System.out.println("另一个方法被调用！");
        }
    }

    @Component
    public class TestCache{

        //TODO 弄清此处缓存不生效的原因
        @Cacheable(value={"testCacheable"},key="#root.method.name",sync = true)
        public String cacheable(String atr){
            System.out.println("方法被调用！");
            new com.fourforfo.fourmall.product.web.TestLockController.OtherCache().testInvokeOtherMethod();
            return atr+UUID.randomUUID().toString();
        }

    }

    @ResponseBody
    @GetMapping("/test_cacheable")
    public String testCacheable(@Param("atr") String atr){

//        String cacheable = .cacheable();

        return testCache.cacheable(atr);
    }


    /**
     * 闭锁CountdownLatch
     * 情形模仿：
     *  放假，门卫锁门
     *  有五个班，只有等五个班都没人才能锁大门
     */
    //锁门
    @ResponseBody
    @GetMapping("/lockDoor")
    public String lockDoor() throws InterruptedException {
        RCountDownLatch door = redissonClient.getCountDownLatch("door");
        door.trySetCount(5);
        door.await();

        return "放假了!人都走完了，锁门了...";

    }

    //id为班级号
    @ResponseBody
    @GetMapping("/goAway/{id}")
    public String lockDoor(@PathVariable("id") Long id) throws InterruptedException {
        RCountDownLatch door = redissonClient.getCountDownLatch("door");
        door.countDown();
        return id+"班的人都走了...";
    }


    /**
     * 使用信号量semaphore来模仿停车场问题
     * 调用go表示车开走，调用park表示将车挺进停车场
     * 使用redis桌面软件给redis添加{"park","3"}，表示停车场有三个车位
     */
    /**
     * 测试信号量semaphore
     */
    @ResponseBody
    @GetMapping("/park")
    public String park() throws InterruptedException {
        RSemaphore park = redissonClient.getSemaphore("park");
//        park.release();//获取信号，获取一个值,没获取到就一致等待
        boolean tryAcquire = park.tryAcquire();//试着获取一下信号，没有就返回false
        return "ok=>"+tryAcquire;
    }
    /**
     * 测试信号量semaphore
     */
    @ResponseBody
    @GetMapping("/go")
    public String go() throws InterruptedException {
        RSemaphore park = redissonClient.getSemaphore("park");
        park.release();
        return "ok";
    }

    /**
     *读写锁保证能一定读到最新数据，修改期间，写锁是一个排他锁（互斥锁），读锁是一个共享锁
     *     写锁没释放读就必须等待
     *     写 + 读：等待写锁释放
     *     写 + 写：堵塞
     *     读 + 写：等待读锁释放
     *     读 + 读：相当于无锁，并发读，只会在redis中记录好，所有当前的读锁，他们都会同时加锁成功
     */
    /**
     * 测试读写锁的读锁
     * @return
     */
    @ResponseBody
    @GetMapping("/read")
    public String read() {

        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("rw-lock");
        RLock rLock = readWriteLock.readLock();

        try {
            rLock.lock();
            System.out.println("读锁加锁成功,执行业务" + Thread.currentThread().getId());
            Thread.sleep(3000);
            String s = redisTemplate.opsForValue().get("writedValue");
            return s;
        } catch (Exception e) {

        } finally {
            rLock.unlock();
            System.out.println("读锁释放成功...");

        }
        return "read error";
    }

    /**
     * 测试读写锁的写锁
     * @return
     */
    @ResponseBody
    @GetMapping("/write")
    public String write() {

        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("rw-lock");
        RLock rLock = readWriteLock.writeLock();

        try {
            rLock.lock();
            System.out.println("写锁加锁成功,执行业务" + Thread.currentThread().getId());
            Thread.sleep(10000);

            String s = UUID.randomUUID().toString();
            redisTemplate.opsForValue().set("writedValue", s);
            return "write success!";
        } catch (Exception e) {

        } finally {
            rLock.unlock();
            System.out.println("写锁释放成功...");


        }
        return "write error";
    }

    /**
     * 重入锁
     * @return
     */
    @ResponseBody
    @GetMapping("/hello")
    public String hello() {
        //1.获取一把锁，锁名一样就是同一把锁
        RLock lock = redissonClient.getLock("my-lock");
        //2.加锁
//        lock.lock();//阻塞式等待
        lock.lock(10, TimeUnit.SECONDS);//10秒自动解锁
        try {
            System.out.println("加锁成功,执行业务" + Thread.currentThread().getId());
            Thread.sleep(30000);

        } catch (Exception e) {

        } finally {
            System.out.println("释放锁..." + Thread.currentThread().getId());
            try {
                lock.unlock();
            } catch (Exception e) {
                System.out.println("锁已被释放，删除锁操作失败");
            }

        }
        return "hello";
    }
}